#include "data_processor.h"
#include "math_support.h"
#include "cal_heart_rate.h"
#include "cal_breathe_rate.h"
#include "record.h"
#include "transmit.h"
#define LOG_SET_LOCAL_LEVEL LOG_LEVEL_INFO
#include "log.h"

#define INHANDQ_CAPACITY 15U
#define WAITQ_CAPACITY 15U
#define DATA_ABS_MAX 10.0F
#define DATA_DIFF_ABS_MAX 10.0F
#define ENOUGH_TO_CAL_HB_RATE 15U

struct {
    Record inHandQ;
    QueueHandle_t waitQ;
} manager;

/**
 * funciton helper define
 * @group {
 */
#define __ASSERT_TRUE(expt) {if(!(expt)) return;}
#define __IS_LEAVE_ADAPTER(segPtr) DPR_is_leave((segPtr)->StInfo.ValMaxABS, (segPtr)->StInfo.DiffMaxABS)
#define __IS_MOVE_ADAPTER(segPtr) DPR_is_move((segPtr)->StInfo.ValMaxABS, (segPtr)->StInfo.DiffMaxABS)
/**
 * @}
 */

/**
 * private funciton
 * @group {
 */
__STATIC_INLINE void dpr_cal_st_info(segment_t *segPtr);
/**
 * @}
 */

/**
 * delegate function
 * @group {
 */

__weak bool DPR_is_leave(float32_t valMaxABS, float32_t diffMaxABS) {
    return false;
}

__weak bool DPR_is_move(float32_t valMaxABS, float32_t diffMaxABS) {
    return false;
}

/**
 * @}
 */

/**
 * callback funciton
 * @group {
 */
__weak void DPR_did_leave(void) {
    // do nothing
    return;
}
__weak void DPR_did_move(void) {
    // do nothing
    return;
}
/**
 * @}
 */

static void _dpr_task(const void *args)
{
    while(true)
    {
        uint8_t data[200U];
        if (xQueueReceive(manager.waitQ, data, portMAX_DELAY) == pdPASS)
        {
            segment_t *segPtr = mem_pool_req();
            assert_param(segPtr != NULL);
            segment_load(segPtr, data, 50U);
            record_push(&manager.inHandQ, segPtr);
        
            LOG_info("Analyze segment(%d)->Len: %d", segPtr->No, segPtr->Len);
            dpr_cal_st_info(segPtr);
        
            /* 判断是否离床 */
            if (__IS_LEAVE_ADAPTER(segPtr)) {
                DPR_did_leave();
                uint32_t qLen = manager.inHandQ.Len;
                for(uint32_t j = 0; j < qLen; j++) {
                    mem_pool_rel(segPtr + j);
                }
                return;
            }
            /* 判断是否体动 */
            if (__IS_MOVE_ADAPTER(segPtr)) {
                DPR_did_move();
                uint32_t qLen = manager.inHandQ.Len;
                for(uint32_t j = 0; j < qLen; j++) {
                    mem_pool_rel(segPtr + j);
                }
                return;
            }
            /* 如果有效数据足够长则计算心率 */
            if (manager.inHandQ.Len >= ENOUGH_TO_CAL_HB_RATE) {
         
                uint32_t tickStart = HAL_GetTick();
            
                static float32_t buff[DATA_BLOCK_SIZE * DATA_BLOCK_CNT];
                static float32_t src[DATA_BLOCK_SIZE];
            
                mid_val_fltr_usr(manager.inHandQ.HeadPtr->DataPtr, src, 750U);
            
                uint32_t heartRate = cal_heart_rate(src, buff);
                uint32_t breatheRate = cal_breathe_rate(src, buff);
			
                LOG_debug("Use time: %dms", HAL_GetTick() - tickStart);
                LOG_prod("Heart rate: %d\r\n", heartRate);
                LOG_prod("Breathe rate: %d\r\n", breatheRate);
			
                record_move_front(&manager.inHandQ);
            }
        }
    }
}

void DPR_init() {
    mem_pool_init();
    record_init(&manager.inHandQ, INHANDQ_CAPACITY, "inHandQ");
    
    manager.waitQ = xQueueCreate(3U, 200U);
    osThreadDef(dprThread, _dpr_task, osPriorityAboveNormal, NULL, 128U);
    osThreadCreate(osThread(dprThread), NULL);
}

void DPR_waitQ_post (void *msgPtr, uint32_t msgLen) {
    xQueueSend(manager.waitQ, msgPtr, portMAX_DELAY);
}

/**
 * @brief 计算该数据段中数据绝对值最大值和相邻数据差值绝对值最大值，并将其保存在数据段统计信息成员中
 * @param 数据段指针
 */
__STATIC_INLINE void dpr_cal_st_info(segment_t *segPtr) {
    float32_t *begPtr = segPtr->DataPtr;
    float32_t prevVal = 0.0F, currVal = 0.0F;
    float32_t prevValABS = 0.0F, currValABS = 0.0F, diffABS = 0.0F;
    float32_t valMaxABS = 0.0F, diffMaxABS = 0.0F;
    uint32_t index = 0U;
    for (; index < segPtr->Len; index++) {
        currVal = *(begPtr + index);
        if (index == 0U) {
            prevVal = currVal;
            continue;
        }
        currValABS = fabsf(currVal);
        prevValABS = fabsf(prevVal);
        valMaxABS = currValABS > valMaxABS ? currValABS : valMaxABS;
        diffABS = fabsf(currValABS - prevValABS);
        diffMaxABS = diffABS > diffMaxABS ? diffABS : diffMaxABS;
        prevVal = currVal;
    }
    segPtr->StInfo.ValMaxABS = valMaxABS;
    segPtr->StInfo.DiffMaxABS = diffMaxABS;
}
